package com.spark.util.client

import com.spark.util.core.{Borrow, Logging}
import com.zaxxer.hikari.HikariDataSource
import javax.sql.DataSource
import scalikejdbc.{ConnectionPool, ConnectionPoolSettings, DB, DataSourceConnectionPool}

object DbPoolClient extends Borrow with Logging {

  // 使用默认连接池
  def init(poolName:String, driver:String, url:String, username:String, password:String): Unit = {
    if(!ConnectionPool.isInitialized(Symbol(poolName))){
      Class.forName(driver)
      val settings = ConnectionPoolSettings(
        initialSize = 3,
        maxSize = 5,
        connectionTimeoutMillis = 3000L,
        validationQuery = "select 1")
      ConnectionPool.add(Symbol(poolName),url,username,password,settings)
      info(s"Initialize connection pool：$poolName")
    }
  }

  // HiKariCP是数据库连接池的一个后起之秀，号称性能最好，可以完美地PK掉其他连接池。
  def initHikariCP(poolName:String, driver:String, url:String, username:String, password:String): Unit = {
    if(!ConnectionPool.isInitialized(Symbol(poolName))){
      val dataSource: DataSource = {
        val ds = new HikariDataSource()
        ds.setDriverClassName(driver)
        ds.setJdbcUrl(url)
        ds.setUsername(username)
        ds.setPassword(password)
        // 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException,缺省:30秒
        ds.setConnectionTimeout(3000)
        // 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟
        ds.setIdleTimeout(600000)
        // 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒
        // 参考MySQL wait_timeout参数(show variables like '%timeout%'; 28800 单位秒 即8个小时))
        ds.setMaxLifetime(1800000)
        // 池中最大连接数，包括闲置和使用中的连接。缺省值：10
        // 推荐的公式：((core_count * 2) + effective_spindle_count)
        ds.setMaximumPoolSize(10)
        // 池中维护的最小空闲连接数
        ds.setMinimumIdle(3)
        ds
      }
      ConnectionPool.add(Symbol(poolName), new DataSourceConnectionPool(dataSource))
      info(s"Initialize connection pool：$poolName")
    }
  }

  def usingDB[A](poolName:String)(execute:DB => A) :A = using(DB(ConnectionPool(Symbol(poolName)).borrow()))(execute)

}
